/*
    Copyright (C) 2009, 2010, 2012  Daniel Gutson, Lucas Paradisi, FuDePAN

    This file is part of the FXP project.

    FXP is free software: you can redistribute it and/or modify
    it under the terms of the GNU General Public License as published by
    the Free Software Foundation, either version 3 of the License, or
    (at your option) any later version.

    FXP is distributed in the hope that it will be useful,
    but WITHOUT ANY WARRANTY; without even the implied warranty of
    MERCHANTABILITY or FITNESS FOR A PARTICULAR PURPOSE.  See the
    GNU General Public License for more details.

    You should have received a copy of the GNU General Public License
    along with FXP.  If not, see <http://www.gnu.org/licenses/>.
*/

#ifndef FXP_INLINE_H
#error Internal header file, DO NOT include this.
#endif


namespace fxp
{

FXPListener::AttributeOutput FXPDumbListener::new_attribute(const string& /*name*/, const string& /*value*/)
{
    return AttributeAccepted;
}

FXPListener* FXPDumbListener::new_child(const string& /*name*/)
{
    return this;
}

bool FXPDumbListener::receive_text(const string& /*text*/)
{
    return true;
}

template <class T>
inline bool parse_xml(
    const char* filename,
    T& root_data,
    FXPRootParser<T>& root_parser,
    ParseErrorData& ped,
    ErrorAction unknownStuff = Ignore,
    ErrorAction malformedStuff = Ignore,
    TextAction textAction = DisableTextProcessing)
{
    extern bool parse_xml(
        const char * filename,
        FXPListener * listener,
        ParseErrorData & ped,
        ErrorAction unknownStuff,
        ErrorAction malformedStuff,
        TextAction textAction);

    return parse_xml(filename, root_parser.get_listener(root_data), ped, unknownStuff, malformedStuff, textAction);
}

template <class T>
inline bool parse_xml(
    const char* filename,
    T& root_data,
    FXPRootParser<T>& root_parser,
    ErrorAction unknownStuff = Ignore,
    ErrorAction malformedStuff = Ignore,
    TextAction textAction = DisableTextProcessing)
{
    ParseErrorData ped;

    return parse_xml(filename, root_data, root_parser, ped, unknownStuff, malformedStuff, textAction);
}

template <class Container>
inline bool parse_xml(
    const char* filename,
    Container& root_data,
    FXPRootParserNonDef<Container>& root_parser,
    ParseErrorData& ped,
    ErrorAction unknownStuff = Ignore,
    ErrorAction malformedStuff = Ignore,
    TextAction textAction = DisableTextProcessing)
{
    extern bool parse_xml(
        const char * filename,
        FXPListener * listener,
        ParseErrorData & ped,
        ErrorAction unknownStuff,
        ErrorAction malformedStuff,
        TextAction textAction);

    return parse_xml(filename, root_parser.get_listener(root_data), ped, unknownStuff, malformedStuff, textAction);
}

template <class Container>
inline bool parse_xml(
    const char* filename,
    Container& root_data,
    FXPRootParserNonDef<Container>& root_parser,
    ErrorAction unknownStuff = Ignore,
    ErrorAction malformedStuff = Ignore,
    TextAction textAction = DisableTextProcessing)
{
    ParseErrorData ped;

    return parse_xml(filename, root_data, root_parser, ped, unknownStuff, malformedStuff, textAction);
}

template <class T>
bool FXParser<T>::acceptable_attr(const string& attr) const
{
    return contains(attr_setters, attr);
}

template <class T>
bool FXParser<T>::accepts_text() const
{
    return text_setter != NULL;
}

template <class T>
bool FXParser<T>::instance_set() const
{
    return instance != NULL;
}

template <class T>
T* FXParser<T>::get_instance() const
{
    return instance;
}

template <class T>
void FXParser<T>::reset_instance()
{
    delete instance;
    instance = NULL;
}

template <class T>
void FXParser<T>::add_attr(const string& attr, AttributeSetter<T>* attr_setter)
{
    attr_setters[attr] = attr_setter;
}

template <class T>
void FXParser<T>::add_child(const string& collection_name, ParentChildBinder<T>* pcb)
{
    children_binders[collection_name] = pcb;
}

template <class T>
void FXParser<T>::set_text_attr(AttributeSetter<T>* setter)
{
    text_setter = setter;
}

template <class T>
FXPListener* FXParser<T>::get_listener(T& t)
{
    instance = &t;
    return this;
}

template <class T>
const std::string& FXParser<T>::get_id() const
{
    return id;
}

template <class T>
FXParser<T>::~FXParser()
{
    delete_container(attr_setters);
    delete_container(children_binders);
    delete text_setter;
}

template <class T>
FXPListener::AttributeOutput FXParser<T>::new_attribute(const string& name, const string& value)
{
    typename std::map<std::string, AttributeSetter<T>*>::iterator it = attr_setters.find(name);

    if (it != attr_setters.end())
    {
        if (it->second->set_value(*instance, value))
            return AttributeAccepted;
        else
            return MalformedAttributeValue;
    }
    else
        return UnknownAttribute;
}

template <class T>
FXPListener* FXParser<T>::new_child(const string& name)
{
    typename std::map<std::string, ParentChildBinder<T>*>::iterator it = children_binders.find(name);

    if (it != children_binders.end())
        return it->second->pass_instance(*instance);
    else
        return NULL;
}

template <class T>
bool FXParser<T>::receive_text(const string& text)
{
    const bool ret = (text_setter != NULL);
    if (ret)
        text_setter->set_value(*instance, text);

    return ret;
}

template <class T, class Constructor>
FXParserNonDef<T, Constructor>::~FXParserNonDef()
{
    delete containerAdapter;
}

template<class T, class Constructor>
template<class ContainerType>
FXPListener* FXParserNonDef<T, Constructor>::get_listener(ContainerType& cont)
{
    this->reset_instance();
    constructor.reset();
    delete containerAdapter;
    containerAdapter = new ContainerAdapterImpl<T, ContainerType>(cont);
    return this;
}

template <class T, class Constructor>
FXPListener::AttributeOutput FXParserNonDef<T, Constructor>::new_attribute(const string& name, const string& value)
{
    if (constructor.accepts(name, value))
        return FXPListener::AttributeAccepted;
    else if (this->acceptable_attr(name))
    {
        insert_into(pending_attrs, std::pair<string, string>(name, value));
        return FXPListener::AttributeAccepted;
    }
    else
        return FXPListener::UnknownAttribute;
}


template <class T, class Constructor>
void FXParserNonDef<T, Constructor>::create_element()
{
    T* const new_element = constructor.create();
    FXParser<T>::get_listener(*new_element);

    /* Commit all the pending stuff */
    FXParser<T>::receive_text(pending_text);
    for (std::list<std::pair<string, string> >::const_iterator it = pending_attrs.begin();
            it != pending_attrs.end();
            ++it)
    {
        FXParser<T>::new_attribute(it->first, it->second);
    }
    pending_attrs.clear();
}

template <class T, class Constructor>
FXPListener* FXParserNonDef<T, Constructor>::new_child(const string& name)
{
    if (!this->instance_set())
        create_element();
    return FXParser<T>::new_child(name);
}

template <class T, class Constructor>
bool FXParserNonDef<T, Constructor>::receive_text(const string& text)
{
    const bool ret = this->accepts_text();
    if (ret)
        pending_text = text;

    return ret;
}

template <class T, class Constructor>
void FXParserNonDef<T, Constructor>::close()
{
    if (this->instance_set())
    {
        containerAdapter->insert(*this->get_instance());
        this->reset_instance();
    }
}

template <class T, class T0>
void Constructor1<T, T0>::set_param0(const string& attr)
{
    attr_name = attr;
}

template <class T, class T0>
void Constructor1<T, T0>::set_param0(const string& attr, const T0& default_value)
{
    attr_name = attr;
    this->default_value = param0 = default_value;
}

template <class T, class T0>
void Constructor1<T, T0>::set_param0_constant(const T0& constant_value)
{
    attr_name.clear();
    default_value = param0 = constant_value;
}

template <class T, class T0>
bool Constructor1<T, T0>::accepts(const string& attr, const string& value)
{
    if (attr == attr_name)
        return from_string(value, param0);
    else
        return false;
}

template <class T, class T0>
T* Constructor1<T, T0>::create()
{
    return new T(param0);
}

template <class T, class T0>
void Constructor1<T, T0>::reset()
{
    param0 = default_value;
}

template <class T0>
void Parameters1<T0>::set_param0(const string& attr)
{
    attr_name = attr;
}

template <class T0>
void Parameters1<T0>::set_param0(const string& attr, const BType& default_value)
{
    attr_name = attr;
    this->default_value = param0 = default_value;
}

template <class T0>
void Parameters1<T0>::set_param0_constant(const BType& constant_value)
{
    attr_name.clear();
    default_value = param0 = constant_value;
}

template <class T0>
bool Parameters1<T0>::accepts(const string& attr, const string& value)
{
    if (attr == attr_name)
        return from_string(value, param0);
    else
        return false;
}

template <class T0>
template <class T, class M>
void Parameters1<T0>::invoke(T* instance, M method)
{
    (instance->*method)(param0);
}

template <class T0>
void Parameters1<T0>::reset()
{
    param0 = default_value;
}

// 1) Attributes

template <class T, class AttrType>
bool AttributeSetterImpl<T, AttrType>::set_value(T& t, const string& value)
{
    return from_string(value, t.*attr);
}

template <class T, class AttrType>
inline void add_attr(FXParser<T>& parser, const string& attr_name, AttrType(T::*attr))
{
    parser.add_attr(attr_name, new AttributeSetterImpl<T, AttrType>(attr));
}

template <class T>
inline void set_text_attr(FXParser<T>& parser, string(T::*attr))
{
    parser.set_text_attr(new AttributeSetterImpl<T, string>(attr));
}

// 1.1) Attributes set with a method:

template <class T, class AttrType, class RetType>
bool AttributeSetterMethodImpl<T, AttrType, RetType>::set_value(T& t, const string& value)
{
    AttrType v;
    const bool ret = from_string(value, v);
    if (ret)
        (t.*method)(v);
    return ret;
}

template <class T, class AttrType, class RetType>
inline void add_attr(FXParser<T>& parser, const string& attr_name, RetType(T::*method)(AttrType))
{
    parser.add_attr(attr_name, new AttributeSetterMethodImpl<T, AttrType, RetType>(method));
}

// 1.2) Ignorable attributes

template <class T>
bool IgnoreAttributeSetter<T>::set_value(T& t, const string& value)
{
    return true;
}

template <class T>
inline void ignore_attr(FXParser<T>& parser, const string& attr_name)
{
    parser.add_attr(attr_name, new IgnoreAttributeSetter<T>());
}

// 2) Containers

template <class T, class ContainerType, class ElementType>
FXPListener* ParentChildBinderImpl<T, ContainerType, ElementType>::pass_instance(T& t)
{
    (t.*container).push_back(ElementType());
    return child_parser.get_listener((t.*container).back());
}

template <class T, class ContainerType, class ElementType>
inline void add_child(FXParser<T>& parser, ContainerType(T::*container), FXParser<ElementType>& child_parser)
{
    parser.add_child(child_parser.get_id(), new ParentChildBinderImpl<T, ContainerType, ElementType>(container, child_parser));
}

// 2.1) NonDefault version

template <class T, class ContainerType, class ElementType, class Constructor>
FXPListener* ParentChildNonDefBinderImpl<T, ContainerType, ElementType, Constructor>::pass_instance(T& t)
{
    return child_parser.get_listener(t.*container);
}

template <class T, class ContainerType, class ElementType, class Constructor>
inline void add_child(FXParser<T>& parser, ContainerType(T::*container), FXParserNonDef<ElementType, Constructor>& child_parser)
{
    parser.add_child(child_parser.get_id(), new ParentChildNonDefBinderImpl<T, ContainerType, ElementType, Constructor>(container, child_parser));
}

// 3) LOVs

bool IgnoringParser::ignorable(const string& attr_name) const
{
    return contains(ignores, attr_name);
}

void IgnoringParser::ignore_attr(const string& attr_name)
{
    insert_into(ignores, attr_name);
}

const std::string& IgnoringParser::get_id() const
{
    return id;
}

inline void ignore_attr(IgnoringParser& parser, const string& attr_name)
{
    parser.ignore_attr(attr_name);
}

template <class Container, class ElementType>
FXPListener* FXParserLOV<Container, ElementType>::get_listener(Container& cont)
{
    c = &cont;
    return this;
}

template <class Container, class ElementType>
FXPListener::AttributeOutput FXParserLOV<Container, ElementType>::new_attribute(const string& name, const string& value)
{
    if (name == xml_field)
    {
        ElementType elem;

        if (from_string(value, elem))
        {
            insert_into(*c, elem); /* Insertion policy here! */
            return AttributeAccepted;
        }
        else
            return MalformedAttributeValue;
    }
    else if (this->ignorable(name))
        return AttributeAccepted;
    else
        return UnknownAttribute;
}

template <class Container, class ElementType>
FXPListener* FXParserLOV<Container, ElementType>::new_child(const string& /*name*/)
{
    return NULL;
}

template <class Container, class ElementType>
bool FXParserLOV<Container, ElementType>::receive_text(const string& /*text*/)
{
    return false;
}


template <class T, class ContainerType, class ElementType>
ParentLovBinderImpl<T, ContainerType, ElementType>::~ParentLovBinderImpl()
{
    if (delete_on_destructor)
        delete lov_parser;
}

template <class T, class ContainerType, class ElementType>
FXPListener* ParentLovBinderImpl<T, ContainerType, ElementType>::pass_instance(T& t)
{
    return lov_parser->get_listener(t.*container);
}

template <class T, class ContainerType, class ElementType>
inline void add_child(FXParser<T>& parser, ContainerType(T::*container), FXParserLOV<ContainerType, ElementType>& lov_parser)
{
    parser.add_child(lov_parser.get_id(), new ParentLovBinderImpl<T, ContainerType, ElementType>(container, lov_parser));
}

// Internal use only
template <class T, class ContainerType, class ElementType>
inline void add_child(FXParser<T>& parser, ContainerType(T::*container), FXParserLOV<ContainerType, ElementType>* lov_parser)
{
    parser.add_child(lov_parser->get_id(), new ParentLovBinderImpl<T, ContainerType, ElementType>(container, lov_parser));
}

//  version for vector, list (containers receiving just 2 params)
template <class T, template <class, class> class ContainerType, class ElementType, class Allocator>
inline void add_lov_child(FXParser<T>& parser, const string& collection_name, const string& attr_name, ContainerType<ElementType, Allocator> (T::*container))
{
    add_child(parser,
              container,
              new FXParserLOV<ContainerType<ElementType, Allocator>, ElementType>(collection_name, attr_name));
}

//  version for sets (containers receiving 3 params)
template <class T, template <class, class, class> class ContainerType, class ElementType, class Allocator, class Comp>
inline void add_lov_child(FXParser<T>& parser, const string& collection_name, const string& attr_name, ContainerType<ElementType, Allocator, Comp> (T::*container))
{
    add_child(parser,
              container,
              new FXParserLOV<ContainerType<ElementType, Allocator, Comp>, ElementType>(collection_name, attr_name));
}

template <class T, class RetType, class Parameters>
FXPListener* FXParserLOVMethod<T, RetType, Parameters>::get_listener(T& t)
{
    parameters.reset();
    instance = &t;
    return this;
}

template <class T, class RetType, class Parameters>
FXPListener::AttributeOutput FXParserLOVMethod<T, RetType, Parameters>::new_attribute(const string& name, const string& value)
{
    if (parameters.accepts(name, value))
        return FXPListener::AttributeAccepted;
    else if (this->ignorable(name))
        return AttributeAccepted;
    else
        return FXPListener::UnknownAttribute;
}

template <class T, class RetType, class Parameters>
FXPListener* FXParserLOVMethod<T, RetType, Parameters>::new_child(const string& /*name*/)
{
    return NULL;
}

template <class T, class RetType, class Parameters>
bool FXParserLOVMethod<T, RetType, Parameters>::receive_text(const string& /*text*/)
{
    return false;
}

template <class T, class RetType, class Parameters>
void FXParserLOVMethod<T, RetType, Parameters>::close()
{
    parameters.invoke(instance, method);
}

template <class T, class ChildParserType>
FXPListener* ParentChildDummyBinder<T, ChildParserType>::pass_instance(T& t)
{
    return child_parser.get_listener(t);
}

template <class T, class RetType, class Parameters>
inline void add_lov_child(FXParser<T>& parser, FXParserLOVMethod<T, RetType, Parameters>& child)
{
    parser.add_child(child.get_id(), new ParentChildDummyBinder<T, FXParserLOVMethod<T, RetType, Parameters> >(child));
}

template <template <class, class> class C, class T, class A >
FXPListener* FXParser<C<T, A> >::get_listener(C<T, A>& t)
{
    instance = &t;
    return this;
}

template <template <class, class> class C, class T, class A >
FXPListener::AttributeOutput FXParser<C<T, A> >::new_attribute(const string& name, const string& /*value*/)
{
    if (this->ignorable(name))
        return AttributeAccepted;
    else
        return UnknownAttribute;
}

template <template <class, class> class C, class T, class A >
FXPListener* FXParser<C<T, A> >::new_child(const string& name)
{
    if (name == this->get_id()/*element_name*/)
    {
        instance->push_back(T());
        return child_parser.get_listener(instance->back());
    }
    else
        return NULL;
}

template <template <class, class> class C, class T, class A >
bool FXParser<C<T, A> >::receive_text(const string& /*text*/)
{
    return false;
}

template <template <class, class> class C, class T, class A >
inline void ignore_attr(FXParser<C<T, A> >& parser, const string& attr_name)
{
    parser.ignore_attr(attr_name);
}

FXPListener::AttributeOutput FXPRootListener::new_attribute(const string& /*name*/, const string& /*value*/)
{
    return UnknownAttribute; /* Never should be called */
}

FXPListener* FXPRootListener::new_child(const string& name)
{
    if (name == root_name)
        return first;
    else
        return NULL;
}

bool FXPRootListener::receive_text(const string& /*text*/)
{
    return false; /* Never should be called */
}

void FXPRootListener::set_first_listener(FXPListener* first_listener)
{
    first = first_listener;
}

template <class T>
FXPListener* FXPRootParser<T>::get_listener(T& t)
{
    root_listener.set_first_listener(FXParser<T>::get_listener(t));
    return &root_listener;
}

template <template <class, class> class C, class T, class A >
FXPListener* FXPRootParser<C<T, A> >::get_listener(C<T, A>& t)
{
    root_listener.set_first_listener(FXParser<C<T, A> >::get_listener(t));
    return &root_listener;
}

template <class Container>
FXPListener::AttributeOutput FXPRootParserNonDef<Container>::new_attribute(const string& name, const string& /*value*/)
{
    if (this->ignorable(name))
        return AttributeAccepted;
    else
        return UnknownAttribute;
}

template <class Container>
FXPListener* FXPRootParserNonDef<Container>::new_child(const string& name)
{
    if (name == child_id)
        return binder->pass_instance(*instance); //child_parser.get_listener(*instance);
    else
        return NULL;
}

template <class Container>
bool FXPRootParserNonDef<Container>::receive_text(const string& /*text*/)
{
    return false;
}

template <class Container>
template <class Constructor>
FXPListener* FXPRootParserNonDef<Container>::_ParentChildBinderImpl<Constructor>::pass_instance(Container& c)
{
    return child_parser.get_listener(c);
}

template <class Container>
FXPListener* FXPRootParserNonDef<Container>::get_listener(Container& t)
{
    instance = &t;
    root_listener.set_first_listener(this);
    return &root_listener;
}

template <class Container>
FXPRootParserNonDef<Container>::~FXPRootParserNonDef()
{
    delete binder;
}

}